home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / memory / switch stack / switch stack.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  10.6 KB  |  322 lines

  1. /*
  2. **    File:        Switch Stack.c
  3. **
  4. **    Contains:    A simple 68K example of a VBL written in C that runs on a
  5. **                private stack. This won't compile for PowerPC, so don't try it.
  6. **                This is a 68K-only thing.
  7. **
  8. **    Written by:    Jim Luther (Based on the VBL code from the Technical Note
  9. **                "TB 35 - MultiFinder Miscellanea".) 
  10. **
  11. **    Copyright:    © 1993-1995 by Apple Computer, Inc., all rights reserved.
  12. **
  13. **    Change History (most recent first):
  14. **
  15. **         <8>     3 Feb 98    Quinn    Expanded comment about holding the private stack.
  16. **         <7>     05/30/95    JML        Added check for VM before using HoldMemory and UnholdMemory.
  17. **         <6>     02/18/95    JML        Added HoldMemory and UnholdMemory calls to keep stack in
  18. **                                    physical memory.
  19. **         <5>     01/27/95    JML        Added .r file for resources
  20. **         <4>     01/27/95    JML        Got rid of the THINK C-isms. Now builds with THINK C, MPW C, and Metrowerks C
  21. **         <3>     11/02/93    JML        Added reentrancy comment to VBL
  22. **         <2>     10/13/93    JML        Minor cleanup
  23. **         <1>     10/08/93    JML        First pass
  24. **
  25. */
  26.  
  27. #include <Types.h>
  28. #include <Memory.h>
  29. #include <QuickDraw.h>
  30. #include <Fonts.h>
  31. #include <Windows.h>
  32. #include <Menus.h>
  33. #include <TextEdit.h>
  34. #include <Dialogs.h>
  35. #include <Events.h>
  36. #include <TextUtils.h>
  37. #include <Retrace.h>
  38. #include <LowMem.h>
  39. #include <Gestalt.h>
  40.  
  41. /*----------------------------------------------------------------------------*/
  42.  
  43. /*
  44. **    Define a struct to keep track of what we need to swap stacks.
  45. **
  46. **    WARNING:    Do not change this structure.  The assembly inlines
  47. **                SwitchtoPrivateStack and RestoreStack depend on the
  48. **                exact order of the fields in this record!
  49. */
  50. struct StackRec
  51. {
  52.     Ptr        ourStackBottom;        /* saved stack bottom where we can find it */
  53.     Ptr        ourStackTop;        /* saved stack top where we can find it */
  54.     Ptr        savedA7;            /* place where VBL saves current A7 */
  55.     Ptr        savedStkLowPt;        /* place where VBL saves StkLowPt */
  56.     Ptr        savedHiHeapMark;    /* place where VBL saves HiHeapMark */
  57. };
  58. typedef struct StackRec StackRec;
  59. typedef StackRec *StackRecPtr;
  60.  
  61. /*
  62. **    Define a struct to keep track of what we need in the VBL.  Put theVBLTask
  63. **    into the struct first because its address will be passed to our VBL task
  64. **    in A0.
  65. */
  66. struct VBLRec
  67. {
  68.     VBLTask        theVBLTask;        /* the VBL task itself */
  69.     long        VBLA5;            /* saved CurrentA5 where we can find it */
  70.     StackRec    stackRecord;
  71. };
  72. typedef struct VBLRec VBLRec;
  73. typedef struct VBLRec *VBLRecPtr;
  74.  
  75. /*----------------------------------------------------------------------------*/
  76.  
  77. /*
  78. **    Constants used in sample
  79. */
  80.  
  81. enum
  82. {
  83.     kInterval        =    6,        /* VBL interval */
  84.     rInfoDialog        =    140,    /* DLOG resource ID */
  85.     rStatTextItem    =    1,        /* item number of counter field in dialog */
  86.     
  87.     kStackSize         =     0x4000    /* 16K */
  88. };
  89.  
  90. /*----------------------------------------------------------------------------*/
  91.  
  92. /*
  93. **    Prototypes
  94. */
  95.  
  96. pascal void    DoVBL (VBLRecPtr recPtr);
  97. void        StartVBL(void);
  98. void        main(void);
  99.  
  100. /*----------------------------------------------------------------------------*/
  101.  
  102. /*
  103. **    A global which will be referenced from our VBL Task and the test program
  104. */
  105.  
  106. long    gCounter;        /* Counter incremented each time our VBL gets called */
  107. long    gVMOn = false;    /* true if System 7 VM is turned on */
  108.  
  109. /*----------------------------------------------------------------------------*/
  110.  
  111. /*
  112. **    GetVBLRec returns the address of the VBLRec associated with our VBL task.
  113. **    This works because on entry into the VBL task, A0 points to the theVBLTask
  114. **    field in the VBLRec record, which is the first field in the record and that
  115. **    is the address we return.  Note that this method works whether the VBLRec
  116. **    is allocated globally, in the heap (as long as the record is locked in 
  117. **    memory) or if it is allocated on the stack as is the case in this example.
  118. **    In the latter case this is OK as long as the procedure which installed the
  119. **    task does not exit while the task is running.  This trick allows us to get
  120. **    to the saved A5, but it could also be used to get to anything we wanted to
  121. **    store in the record.
  122. */
  123. extern    VBLRecPtr GetVBLRec(void)
  124.     = 0x2008;    /* MOVE.L    A0,D0 */
  125.  
  126. /*
  127. **    SwitchtoPrivateStack and RestoreStack are assembly language inlines
  128. **    that perform the stack switch using the StackRec passed as a parameter
  129. **    to SwitchtoPrivateStack.
  130. */
  131.  
  132. #pragma parameter SwitchtoPrivateStack(__A0)
  133. extern    void    SwitchtoPrivateStack(StackRec *sr)
  134.     =    {                            /*                                                    ; A0 = the StackRecPtr parameter */
  135.             0x2178, 0x0BAE, 0x0010,    /* MOVE.L    HiHeapMark,StackRec.savedHiHeapMark(A0)    ; save HiHeapMark */
  136.             0x2178, 0x0110, 0x000C,    /* MOVE.L    StkLowPt,StackRec.savedStkLowPt(A0)        ; save StkLowPt */
  137.             0x214f, 0x0008,            /* MOVE.L    SP,StackRec.savedA7(A0)                    ; save current system stack */
  138.             0x42b8, 0x0110,            /* CLR.L    StkLowPt                                ; disable the stack sniffer */
  139.             0x21D0, 0x0BAE,            /* MOVE.L    StackRec.ourStackBottom(A0),HiHeapMark    ; set HiHeapMark to bottom of our stack */
  140.             0x2e68, 0x0004,            /* MOVEA.L    StackRec.ourStackTop(A0),SP                ; switch stacks */
  141.             0x2f08                    /* MOVE.L    A0,-(SP)                                ; save A0 (*sr) on top of private stack */
  142.         };
  143.  
  144. extern    void    RestoreStack(void)
  145.     =    {
  146.             0x205F,                    /* MOVE.L    (SP)+,A0                                ; restore A0 (*sr) from private stack */
  147.             0x2E68, 0x0008,            /* MOVEA.L    StackRec.savedA7(A0),A7                    ; restore system stack */
  148.             0x21E8, 0x0010, 0x0BAE,    /* MOVE.L    StackRec.savedHiHeapMark(A0),HiHeapMark    ; restore HiHeapMark */
  149.             0x21E8, 0x000C, 0x0110    /* MOVE.L    StackRec.savedStkLowPt(A0),StkLowPt        ; restore the sniffer */
  150.         };
  151.  
  152. /*----------------------------------------------------------------------------*/
  153.  
  154. /*
  155. **    DoVBL is called only by StartVBL()
  156. **
  157. **    WARNING:    It MUST be declared "pascal" so the function will remove the
  158. **                parameters itself!
  159. */
  160. pascal    void DoVBL (VBLRecPtr recPtr)
  161. {
  162.     gCounter++;                                    /* Show we can set a global */
  163.     recPtr->theVBLTask.vblCount = kInterval;    /* Set ourselves to run again */
  164. }
  165.  
  166. /*----------------------------------------------------------------------------*/
  167.  
  168. /*
  169. **    This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  170. **    and properly set up A5.  Having done that, switches to a private stack and
  171. **    then  calls DoVBL to increment a global counter and sets itself to run again.
  172. **    Because of the vagaries of C optimization, it calls a separate routine to
  173. **    actually access global variables.  See "OV 10 - Setting and Restoring A5"
  174. **    for the reasons for this, as well as for a description of SetA5.
  175. **    I can switch to my private stack without checking to make sure I haven't
  176. **    already done so because VBLs don't have to worry about begin reentrant.
  177. */
  178. void StartVBL ()
  179. {
  180.     long        curA5;
  181.     VBLRecPtr    recPtr;
  182.     
  183.     recPtr = GetVBLRec();            /* First get our record */
  184.     curA5 = SetA5(recPtr->VBLA5);    /* Get the saved A5 */
  185.     /* Now we can access globals */
  186.  
  187.     /* Switch to private stack */
  188.     SwitchtoPrivateStack(&recPtr->stackRecord);
  189.     
  190.     /*    Now we're running on our private stack */
  191.     
  192.     /*
  193.     **    Make sure any functions called at this point use pascal calling conventions
  194.     **    so the functions clean up the stack before returning!
  195.     */
  196.     DoVBL(recPtr);                    /* Call another routine to do actual work */
  197.  
  198.     /* Switch back to original stack */
  199.     RestoreStack();
  200.  
  201.     (void) SetA5(curA5);            /* Restore old A5 */
  202. }
  203.  
  204. /*----------------------------------------------------------------------------*/
  205.  
  206. /*
  207. **    Create a dialog just to demonstrate that the global variable
  208. **    is being updated by the VBL Task.  Before installing the VBL, we store
  209. **    our A5 in the actual VBL Task record, using SetCurrentA5 described in
  210. **    OV 10 - Setting and Restoring A5.  We also store the location of our
  211. **    private stack which the VBL code will run under. We'll run the VBL,
  212. **    showing the counter being incremented, until the mouse button is clicked.
  213. **    Then we remove the VBL Task, close the dialog, and remove the mouse down
  214. **    events to prevent the application from being inadvertently switched
  215. **    by MultiFinder.
  216. */
  217. void main (void)
  218. {
  219.     VBLRec            theVBLRec;
  220.     DialogPtr        infoDPtr;
  221.     DialogRecord    infoDStorage;
  222.     Str255            numStr = "\pTest";
  223.     OSErr            theErr;
  224.     Handle            theItemHandle;
  225.     short            theItemType;
  226.     Rect            theRect;
  227.     long            lastCount = 0;
  228.     long            response;
  229.  
  230.     InitGraf(&qd.thePort);
  231.     InitFonts();
  232.     InitWindows();
  233.     InitMenus();
  234.     TEInit();
  235.     InitDialogs(NULL);
  236.     InitCursor();
  237.     MaxApplZone();
  238.     
  239.     /* check for VM */
  240.     if ( Gestalt(gestaltVMAttr, &response) == noErr )
  241.     {
  242.         gVMOn = ((response & (1L << gestaltVMPresent)) != 0);
  243.     }
  244.     
  245.     /* Store the current value of A5 in the VBLA5 field. */
  246.     theVBLRec.VBLA5 = SetCurrentA5 ();
  247.  
  248.     /* Allocate memory for our private stack and store it's location in ourStackBottom */
  249.     theVBLRec.stackRecord.ourStackBottom = NewPtrSys(kStackSize);
  250.     if ( theVBLRec.stackRecord.ourStackBottom != NULL )
  251.     {
  252.         if ( gVMOn )
  253.         {
  254.             /*    Make sure the stack is held in physical memory. Otherwise we could
  255.                 end up running interrupt time code on a pageable stack. This would
  256.                 be bad, because a page fault allocating stack space would attempt
  257.                 to create a exception frame on the same interrupt stack, which would
  258.                 cause a double bus fault.  See DTS Technote 1094 for details.
  259.             */
  260.             theErr = HoldMemory(theVBLRec.stackRecord.ourStackBottom, kStackSize);
  261.         }
  262.         else
  263.         {
  264.             theErr = noErr;
  265.         }
  266.         if ( theErr == noErr )
  267.         {
  268.             /* Store location of our stack's top in ourStackTop */
  269.             theVBLRec.stackRecord.ourStackTop =
  270.                 (Ptr)((unsigned long)theVBLRec.stackRecord.ourStackBottom + kStackSize);
  271.             
  272.             gCounter = 0;    /* Initialize our global counter */
  273.             
  274.             /* Put up the dialog */
  275.             infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  276.             DrawDialog (infoDPtr);
  277.             GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, &theRect);
  278.             
  279.             /* Set the address of our routine */
  280.             theVBLRec.theVBLTask.vblAddr = NewVBLProc(StartVBL);
  281.             theVBLRec.theVBLTask.vblCount = kInterval;    /* Frequency of task, in ticks */
  282.             theVBLRec.theVBLTask.qType = vType;            /* qElement is a VBL task */
  283.             theVBLRec.theVBLTask.vblPhase = 0;
  284.             
  285.             /* Now install the VBL task */
  286.             theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
  287.             
  288.             /* Display the counter until the mouse button is pushed */
  289.             if ( theErr == noErr )
  290.             {
  291.                 do
  292.                 {
  293.                     if (gCounter != lastCount)
  294.                     {
  295.                         lastCount = gCounter;
  296.                         NumToString(gCounter, numStr);
  297.                         SetIText(theItemHandle, numStr);
  298.                     }
  299.                 } while ( !Button () );
  300.                 theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
  301.             }
  302.             
  303.             /* Finish up */
  304.             CloseDialog (infoDPtr);        /* Get rid of our dialog */
  305.             FlushEvents (mDownMask, 0);    /* Flush all mouse down events */
  306.             
  307.             if ( gVMOn )
  308.             {
  309.                 /* Release the hold on our stack */
  310.                 theErr = UnholdMemory(theVBLRec.stackRecord.ourStackBottom, kStackSize);
  311.             }
  312.             else
  313.             {
  314.                 theErr = noErr;
  315.             }
  316.         }
  317.         
  318.         /* Dispose of the private stack */
  319.         DisposePtr(theVBLRec.stackRecord.ourStackBottom);
  320.     }
  321. }
  322.